﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Linq;
using Glodon.Gdmp.DB;
using Glodon.Gdmp.UI;

namespace Glodon.Gdmp.SDK.Samples.DrawFilledRegion
{
    /// <summary>
    /// 通过ExternalAction接口实现绘制填充区域的交互行为
    /// </summary>
    internal class DrawExternalAction : ExternalAction
    {
        #region Class Members
        /// <summary>
        /// 填充区域的起始点
        /// </summary>
        private Vector3d _startPoint = null;

        /// <summary>
        /// 填充区域的轮廓线
        /// </summary>
        private List<Curve3d> _curve3dList = new List<Curve3d>();

        /// <summary>
        /// 当前文档
        /// </summary>
        private Document _document = Glodon.Gdmp.DB.Application.Get().ActiveDocument;
        #endregion

        //鼠标左键按下确认填充区域轮廓顶点
        /// <summary>
        /// 外部事件启动后，初始化事件
        /// </summary>
        /// <param name="context"></param>
        public override void OnInitialized(ActionContext context)
        {
            context.CursorType = CursorType.CrossHairCursor;
            context.MouseTooltip = "选择轮廓点";
            context.PromptMessage = "绘制填充区域";
        }

        /// <summary>
        /// 响应鼠标左键按下，记住填充区域轮廓顶点
        /// </summary>
        /// <param name="context"></param>
        /// <param name="position"></param>
        /// <param name="button"></param>
        public override void OnMouseButtonDown(ActionContext context, Vector3d position, MouseChangedButton button)
        {         
            if (button == MouseChangedButton.Left)
            {               
                if (_startPoint != null)
                {
                    if (_startPoint.IsAlmostEqualTo(position))
                        return;
                    var newLine = Line3d.CreateBound(_startPoint, position);
                    _curve3dList.Add(newLine);
                }
                _startPoint = position;
            }
        }

        /// <summary>
        /// 响应鼠标右键抬起，完成操作流程
        /// </summary>
        /// <param name="context"></param>
        /// <param name="position"></param>
        /// <param name="button"></param>
        public override void OnMouseButtonUp(ActionContext context, Vector3d position, MouseChangedButton button)
        {
            // 右键退出
            if (button == MouseChangedButton.Right)
            {
                context.Escape();
            }
        }

        /// <summary>
        /// 响应鼠标移动事件，当鼠标移动时，不停绘制预览填充区域
        /// </summary>
        /// <param name="context"></param>
        /// <param name="position"></param>
        public override void OnMouseMove(ActionContext context, Vector3d position)
        {
            if (_startPoint == null)
            {
                return;
            }

            if (_startPoint.IsAlmostEqualTo(position))
            {
                return;
            }

            //
            // 清空之前临时图形
            context.CleanupTempShape();
            //
            // 根据轮廓线顶点信息，重新绘制预览
            var shape = GraphicsElementShape.Create();
            if (_curve3dList.Count == 0)
            {
                var newLine = Line3d.CreateBound(_startPoint, position);
                shape.AddNode(GraphicsCurve.Create(newLine));
            }
            else
            {
                try
                {
                    var newLine = Line3d.CreateBound(_startPoint, position);
                    var endLine = Line3d.CreateBound(position, _curve3dList[0].StartPoint);
                    var tempCurve3d = new List<Curve3d>();
                    _curve3dList.ForEach(line => tempCurve3d.Add(line));
                    tempCurve3d.Add(newLine);
                    tempCurve3d.Add(endLine);
                    var loopList = new List<CurveLoop>();
                    loopList.Add(CurveLoop.Create(tempCurve3d)); 
                    Vector3d vectorAB = tempCurve3d[0].StartPoint - tempCurve3d[0].EndPoint;
                    Vector3d vectorBC = tempCurve3d[1].StartPoint - tempCurve3d[1].EndPoint;
                    Vector3d normalVector = vectorAB.CrossProduct(vectorBC).Normalize();
                    var extrusion = GeometryCreationUtils.CreateExtrusion(loopList, normalVector, 1);
                    var extrusionBody = GraphicsBody.Create(extrusion);
                    shape.AddNode(extrusionBody);
                }
                catch (Exception)
                {
                    _curve3dList.ForEach(line => shape.AddNode(GraphicsCurve.Create(line)));
                    var newLine = Line3d.CreateBound(_startPoint, position);
                    shape.AddNode(GraphicsCurve.Create(newLine));
                }
            }
            //
            // 绘制临时图形并刷新视图
            context.DrawTempShape(shape);
            context.UpdateView();
        }

        //外部行为结束后，调用该接口
        /// <summary>
        /// UI操作结束后进行填充区域对象的绘制
        /// </summary>
        /// <param name="context"></param>
        public override void OnFinished(ActionContext context)
        {
            context.CleanupTempShape();
            context.CursorType = CursorType.ArrowCursor;
            if (_curve3dList.Count < 2)
                return;

            using (var transaction = new Transaction(_document, "绘制填充区域"))
            {
                try
                {
                    transaction.Start();
                    //
                    // 补充最后一条线段，形成闭合区域
                    if (!_curve3dList[_curve3dList.Count - 1].EndPoint.IsAlmostEqualTo(_curve3dList[0].StartPoint))
                    {
                        var endLine = Line3d.CreateBound(_curve3dList[_curve3dList.Count - 1].EndPoint, _curve3dList[0].StartPoint);
                        _curve3dList.Add(endLine);
                    }
                    var loopList = new List<CurveLoop>();
                    loopList.Add(CurveLoop.Create(_curve3dList));
                    Vector3d vectorAB = _curve3dList[0].StartPoint - _curve3dList[0].EndPoint;
                    Vector3d vectorBC = _curve3dList[1].StartPoint - _curve3dList[1].EndPoint;
                    Vector3d normalVector = vectorAB.CrossProduct(vectorBC).Normalize();
                    var plane = Plane.CreateByNormalAndOrigin(normalVector, _curve3dList[0].StartPoint);
                    var modelViewId = UIApplication.Get().ActiveUIDocument.CurrentUIView.ModelViewId;
                    //
                    // 使用默认填充区域类型进行实例创建并设置填充区域颜色
                    var fillPattern = new ElementCollector(_document).OfCategoryId(BuiltInCategory.BICU_HATCH_PATTERN).OfType<FillPattern>().LastOrDefault();
                    var filledRegion = FilledRegion.Create(_document, fillPattern.Id, modelViewId, plane, loopList);
                    filledRegion.SmartColorMode = SmartColorMode.Invalid;
                    filledRegion.Color = new Color(255, 255, 0);
                    transaction.Commit();
                }
                catch (Exception ex)
                {
                    transaction.RollBack();
                    MessageBoard.Show(ex.Message, "GDMPLab");
                }
                context.UpdateView();
            }
        }
    }
}
